from fastapi import APIRouter, Depends, UploadFile, File, HTTPException, Form
from fastapi.responses import JSONResponse
from typing import Optional, List, Set
import logging
import time
import os
import io
import re
from pathlib import Path
from backend.common.minio import MinioClient, get_minio_client
from backend.common.jwt import get_current_user
from backend.models.user import User

router = APIRouter(prefix="/upload", tags=["文件上传"])

logger = logging.getLogger(__name__)

# 创建MinioClient实例
minio_client = get_minio_client()

# 允许的文件扩展名
ALLOWED_EXTENSIONS: Set[str] = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'}

def sanitize_filename(filename: str) -> str:
    """
    安全处理文件名，移除不安全字符
    
    Args:
        filename: 原始文件名
        
    Returns:
        str: 安全处理后的文件名
    """
    # 获取文件名和扩展名
    stem = Path(filename).stem
    suffix = Path(filename).suffix
    
    # 移除不安全字符，只保留字母、数字、下划线、连字符和点
    stem = re.sub(r'[^\w\-\.]', '_', stem)
    
    # 确保文件名不为空
    if not stem:
        stem = 'file'
        
    return f"{stem}{suffix}"

def allowed_file(filename: str) -> bool:
    """
    检查文件扩展名是否允许
    
    Args:
        filename: 文件名
        
    Returns:
        bool: 文件扩展名是否允许
    """
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@router.post("/avatar")
async def upload_avatar(
    file: UploadFile = File(...),
    current_user: User = Depends(get_current_user)
):
    try:
        if not file.filename:
            raise HTTPException(status_code=400, detail="未选择文件")

        if not file.content_type.startswith('image/'):
            raise HTTPException(status_code=400, detail="只能上传图片文件")

        if not allowed_file(file.filename):
            raise HTTPException(
                status_code=400, 
                detail=f"不支持的文件类型，允许的类型: {', '.join(ALLOWED_EXTENSIONS)}"
            )

        # 安全文件名
        filename = sanitize_filename(file.filename)

        timestamp = int(time.time())
        name_parts = filename.rsplit('.', 1)
        unique_filename = f"{name_parts[0]}_{timestamp}.{name_parts[1]}"

        # ✅ 只读一次
        file_content = await file.read()
        file_size = len(file_content)

        if file_size > 2 * 1024 * 1024:  # 2MB
            raise HTTPException(status_code=400, detail="文件大小不能超过2MB")

        # 上传到 MinIO
        path = "avatars"
        object_name = minio_client.upload_bytes(
            path=path,
            filename=unique_filename,
            data=file_content,
            content_type=file.content_type
        )

        url = minio_client.get_presigned_url(path, unique_filename)

        return JSONResponse(
            status_code=200,
            content={
                "code": 200,
                "message": "图片上传成功",
                "data": {
                    "url": url,
                    "filename": unique_filename,
                    "object_name": object_name,
                    "size": file_size   # ✅ 使用 file_size
                }
            }
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"上传头像失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")


@router.post("/file")
async def upload_file(
    file: UploadFile = File(...),
    folder: str = Form("files"),
    current_user: User = Depends(get_current_user)
):
    """
    上传通用文件
    
    Args:
        file: 上传的文件
        folder: 存储的文件夹路径
        current_user: 当前用户
        
    Returns:
        JSONResponse: 包含上传结果的响应
    """
    try:
        # 检查文件名是否为空
        if not file.filename:
            raise HTTPException(status_code=400, detail="未选择文件")
        
        # 安全处理文件名
        filename = sanitize_filename(file.filename)
        
        # 添加时间戳避免文件名冲突
        timestamp = int(time.time())
        name_parts = filename.rsplit('.', 1) if '.' in filename else [filename, '']
        unique_filename = f"{name_parts[0]}_{timestamp}.{name_parts[1]}" if name_parts[1] else f"{name_parts[0]}_{timestamp}"
        
        # 读取文件内容
        file_content = await file.read()
        file_size = len(file_content)
        
        # 检查文件大小
        max_size = 10 * 1024 * 1024  # 10MB
        if file_size > max_size:
            raise HTTPException(status_code=400, detail=f"文件大小不能超过10MB")
        
        # 重置文件指针
        await file.seek(0)
        
        # 读取文件内容
        file_content = await file.read()
        
        # 上传到MinIO
        object_name = minio_client.upload_bytes(
            path=folder,
            filename=unique_filename,
            data=file_content,
            content_type=file.content_type
        )
        
        # 使用相对路径而不是预签名URL，避免URL过长
        url = f"/api/v1/files/{folder}/{unique_filename}"
        
        return JSONResponse(
            status_code=200,
            content={
                "code": 200,
                "message": "文件上传成功",
                "data": {
                    "url": url,
                    "filename": unique_filename,
                    "object_name": object_name,
                    "size": len(file_content),
                    "content_type": file.content_type
                }
            }
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"上传文件失败: {str(e)}")
        raise HTTPException(status_code=500, detail=f"上传失败: {str(e)}")
